package helper.base

import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.addIfAbsent
import helper.base.string.ToStrHelper.IToStr
import helper.base.string.ToStrHelper.TabStringUnit.Companion.makeTabStringUnit
import helper.base.string.ToStrHelper.toSBStr

typealias A2LHM<A1, A2> = LinkedHashMap<A1, A2>

object LHMHelper {
    object LHMExpand {
        fun <A1, A2> Array<out Pair<A1, A2>>.toA2LHM(): A2LHM<A1, A2> {
            return A2LHM<A1, A2>().also { lhm ->
                this.toList().forEach { (s, d): Pair<A1, A2> ->
                    lhm[s] = d
                }
            }
        }

        fun <A1, A2> ArrayList<out Pair<A1, A2>>.toA2LHM(): A2LHM<A1, A2> {
            return this.toTypedArray().toA2LHM()
        }

        fun A2LHM<String, Double>.addAll(lhm: A2LHM<String, Double>) {
            lhm.map {
                this[it.key] = it.value
            }
        }

        fun <A1, A2, A3> A2LHM<A1, A2>.toA1A3LHM(
            transform: (A2) -> A3
        ): A2LHM<A1, A3> {
            val outputMap = A2LHM<A1, A3>()
            for ((key, value) in this) {
                outputMap[key] = transform(value)
            }
            return outputMap
        }

        fun <A1> A2LHM<A1, Int>.toA1DoubleLHM(
        ): A2LHM<A1, Double> {
            return this.toA1A3LHM {
                it.toDouble()
            }
        }
    }

    //确定了，不为A2LHM额外建立class
    object A2LHMExpand {
        // A2LHM的深拷贝方法
        fun A2LHM<String, String>.deepClone(): A2LHM<String, String> {
            val newMap = A2LHM<String, String>()
            for ((key, value) in this) {
                newMap[key] = value // String是不可变的，所以直接赋值即可
            }
            return newMap
        }

        fun <A1, A2> makeA2LHM(): A2LHM<A1, A2> {
            return A2LHM()
        }

        fun <A1, A2> A2LHM<A1, A2>.toStr(tabNum: Int = 0): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            this.forEach { (a1, a2) ->
                a1.toSBStr(sb, tsu, 0)
                a2.toSBStr(sb, tsu, 1)
            }
            return sb.toString()
        }

        fun <A1, A2> A2LHM<A1, A2>.add(
            a1: A1,
            a2: A2,
        ) {
            this[a1] = a2
        }

        fun <A1, A2> A2LHM<A1, A2>.addIfAbsent(
            a1: A1,
            a2: A2,
        ) {
            this[a1] = this[a1] ?: a2
        }

        fun <A1, A2> A2LHM<A1, A2>.touch(
            init: (A1, A2) -> Unit,
        ) {
            this.forEach { (a1, a2) ->
                init(a1, a2)
            }
        }

        fun <A1, A2> A2LHM<A1, ArrayList<A2>>.addList(
            a1: A1,
            a2: ArrayList<A2>,
        ) {
            this.getOrPut(a1) {
                ArrayList()
            }.addAll(a2)
        }

        fun <A1, A2> A2LHM<A1, ArrayList<A2>>.addList(
            a1: A1,
            vararg a2: A2,
        ) {
            this.getOrPut(
                a1
            ) {
                ArrayList()
            }.addAll(a2)
        }

        fun <E> List<A2LHM<E, Double>>.merge(): A2LHM<E, Double> {
            val result = A2LHM<E, Double>()
            this.forEach { map ->
                map.forEach { (key, value) ->
                    result.merge(key, value) { oldValue, newValue ->
                        oldValue + newValue
                    }
                }
            }
            return result
        }

        fun <E, F> A2LHM<E, LinkedHashSet<F>>.toOneToOneMap(
        ): A2LHM<E, F> {
            val newMap = makeA2LHM<E, F>()
            for ((key, valueSet) in this) {
                require(valueSet.size == 1) {
                    "每个 LinkedHashSet 必须仅包含一个元素"
                }
                newMap[key] = valueSet.first()
            }
            return newMap
        }

        fun <E> A2LHM<E, String>.toEDoubleLHM(): A2LHM<E, Double> {
            val res = makeA2LHM<E, Double>()
            this.forEach { (t, u) ->
                res.add(t, u.toDouble())
            }
            return res
        }
    }

    open class A3LHM<A1, A2, A3> : A2LHM<A1, A2LHM<A2, A3>>() {
        companion object {
            fun <A1, A2, A3> makeA3LHM(): A3LHM<A1, A2, A3> = A3LHM()

            fun <A1, A2, A3> A3LHM<A1, A2, A3>.addAll(
                a3LHM: A3LHM<A1, A2, A3>,
            ) {
                a3LHM.touch { a1, a2, a3 ->
                    this.addIfAbsent(a1, a2, a3)
                }
            }
        }

        fun add(a1: A1, a2: A2, a3: A3) {
            this.getOrPut(a1) {
                A2LHM()
            }.add(a2, a3)
        }

        //A3是链表的时候，用这个，用以避免重复添加链条
        fun addIfAbsent(a1: A1, a2: A2, a3: A3) {
            this.getOrPut(a1) {
                A2LHM()
            }.addIfAbsent(a2, a3)
        }

        operator fun get(a1: A1, a2: A2): A3? {
            return this[a1]?.get(a2)
        }

        open fun touch(init: (A1, A2, A3) -> Unit) {
            this.forEach { (a1, a1v) ->
                a1v.forEach { (a2, a3) ->
                    init(a1, a2, a3)
                }
            }
        }

        open fun toStr(tabNum: Int = 0): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            this.touch { a1, a2, a3 ->
                a1.toSBStr(sb, tsu, 0)
                a2.toSBStr(sb, tsu, 1)
                a3.toSBStr(sb, tsu, 2)
            }
            return sb.toString()
        }
    }

    open class A4LHM<A1, A2, A3, A4> : A2LHM<A1, A3LHM<A2, A3, A4>>(), IToStr {
        companion object {
            fun <A1, A2, A3, A4> makeA4LHM(): A4LHM<A1, A2, A3, A4> {
                return A4LHM()
            }
        }

        fun addIfAbsent(
            a1: A1,
            a2: A2,
            a3: A3,
            a4: A4,
        ) {
            this.getOrPut(a1) {
                A3LHM()
            }.addIfAbsent(a2, a3, a4)
        }

        open fun touch(
            init: (A1, A2, A3, A4) -> Unit,
        ) {
            this.forEach { (a1, a1v) ->
                a1v.touch { a2, a3, a4 ->
                    init(a1, a2, a3, a4)
                }
            }
        }

        fun toPrintln() {
            this.touch { a1, a2, a3, a4 ->
                println("${a1},${a2},${a3},${a4}")
            }
        }

        override fun toStr(tabNum: Int): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            this.touch { a1, a2, a3, a4 ->
                sb.append("${tsu.tabNumStr0}${a1}\n")
                sb.append("${tsu.tabNumStr1}${a2}\n")
                sb.append("${tsu.tabNumStr2}${a3}\n")
                sb.append("${tsu.tabNumStr3}${a4}\n")
            }
            return sb.toString()
        }
    }

    open class A5LHM<A1, A2, A3, A4, A5> : A2LHM<A1, A4LHM<A2, A3, A4, A5>>() {
        fun addIfAbsent(
            a1: A1,
            a2: A2,
            a3: A3,
            a4: A4,
            a5: A5,
        ) {
            this.getOrPut(a1) {
                A4LHM()
            }.addIfAbsent(a2, a3, a4, a5)
        }

        fun touch(
            init: (A1, A2, A3, A4, A5) -> Unit,
        ) {
            this.forEach { (a1, a1v) ->
                a1v.touch { a2, a3, a4, a5 ->
                    init(a1, a2, a3, a4, a5)
                }
            }
        }
    }
}
